package pentago.engine;

public class BitBoard {

    public long firstBoard;
    public long secondBoard;

    public BitBoard() {
	firstBoard = (long) 0;
	secondBoard = (long) 0;
    }

    public BitBoard(long firstBoard, long secondBoard) {
	this.firstBoard = firstBoard;
	this.secondBoard = secondBoard;
    }

    public BitBoard(BitBoard bitBoard) {
	this.firstBoard = bitBoard.firstBoard;
	this.secondBoard = bitBoard.secondBoard;
    }

    public BitBoard(byte[][] boardState) {

	this();

	long mask = 1;
	for (int y = 5; y >= 0; --y) {
	    for (int x = 5; x >= 0; --x) {
		if (boardState[x][y] == GameEngine.FIRST_PLAYER)
		    firstBoard |= mask;
		if (boardState[x][y] == GameEngine.SECOND_PLAYER)
		    secondBoard |= mask;
		mask <<= 1;
	    }
	}
    }

    public void setPoistion(int position, int player) {
	if (player == GameEngine.FIRST_PLAYER) {
	    // assert (firstBoard & (((long) 1) << position - 1)) == 0;
	    firstBoard |= ((long) 1) << position - 1;
	} else {
	    // assert (secondBoard & (((long) 1) << position - 1)) == 0;
	    secondBoard |= ((long) 1) << position - 1;
	}
    }

    public void rotatePlate(byte platePosition, byte direction) {
	long boardMask;
	long erasureMask;
	long fixMask = 63;
	long boardBits;
	int shift;
	int[][] originalPlateVals;
	int[][] rotatedPlateVals;

	switch (platePosition) {
	case GameBoard.LOWER_LEFT:
	    shift = 3;
	    break;
	case GameBoard.UPPER_RIGHT:
	    shift = 18;
	    break;
	case GameBoard.UPPER_LEFT:
	    shift = 21;
	    break;
	default:
	    shift = 0;
	}

	// Rotate first player:

	boardBits = firstBoard;
	boardMask = 1 << shift;
	originalPlateVals = extractArrayPlateVals(boardBits, boardMask);

	rotatedPlateVals = rotatePlate(originalPlateVals, direction);

	erasureMask = (long) 7 << shift;			// 000...000111 shifted left
	erasureMask = ~erasureMask;				// Negation of all bits
	for (int i = 0; i < 3; ++i) {
	    firstBoard &= erasureMask;
	    erasureMask <<= 6;
	    erasureMask |= fixMask;
	    // assert (erasureMask & ((long) 63)) != 0;
	}

	boardMask = 1 << shift;
	boardBits = extractBitPlateVals(rotatedPlateVals, boardMask);
	firstBoard |= boardBits;

	// Rotate second player:

	boardBits = secondBoard;
	boardMask = 1 << shift;
	originalPlateVals = extractArrayPlateVals(boardBits, boardMask);

	rotatedPlateVals = rotatePlate(originalPlateVals, direction);

	erasureMask = 7 << shift;				// 000...000111 shifted left
	erasureMask = ~erasureMask;				// Negation of all bits
	for (int i = 0; i < 3; ++i) {
	    secondBoard &= erasureMask;
	    erasureMask <<= 6;
	    erasureMask |= fixMask;
	    // assert (erasureMask & ((long) 63)) != 0;
	}

	boardMask = 1 << shift;
	boardBits = extractBitPlateVals(rotatedPlateVals, boardMask);
	secondBoard |= boardBits;
    }

    public void rotatePlateExperiment(byte platePosition, byte direction) {
	long boardMask;
	long erasureMask;
	long fixMask = 63;
	long boardBits;
	int shift;
	// int[][] originalPlateVals;
	// int[][] rotatedPlateVals;

	switch (platePosition) {
	case GameBoard.LOWER_LEFT:
	    shift = 3;
	    break;
	case GameBoard.UPPER_RIGHT:
	    shift = 18;
	    break;
	case GameBoard.UPPER_LEFT:
	    shift = 21;
	    break;
	default:
	    shift = 0;
	}

	// Rotate first player:

	boardBits = firstBoard;
	erasureMask = (long) 7 << shift;			// 000...000111 shifted left
	erasureMask = ~erasureMask;				// Negation of all bits
	for (int i = 0; i < 3; ++i) {
	    firstBoard &= erasureMask;
	    erasureMask <<= 6;
	    erasureMask |= fixMask;
	    // assert (erasureMask & ((long) 63)) != 0;
	}
	boardMask = 1 << shift;
	firstBoard = rotateBits(firstBoard, boardBits, boardMask, direction);
	// firstBoard |= boardBits;
	// originalPlateVals = extractArrayPlateVals(boardBits, boardMask);
	//
	// rotatedPlateVals = rotatePlate(originalPlateVals, direction);
	//
	// boardMask = 1 << shift;
	// boardBits = extractBitPlateVals(rotatedPlateVals, boardMask);
	// firstBoard |= boardBits;

	// Rotate second player:

	boardBits = secondBoard;
	erasureMask = 7 << shift;				// 000...000111 shifted left
	erasureMask = ~erasureMask;				// Negation of all bits
	for (int i = 0; i < 3; ++i) {
	    secondBoard &= erasureMask;
	    erasureMask <<= 6;
	    erasureMask |= fixMask;
	    // assert (erasureMask & ((long) 63)) != 0;
	}
	boardMask = 1 << shift;
	secondBoard = rotateBits(secondBoard, boardBits, boardMask, direction);
	// originalPlateVals = extractArrayPlateVals(boardBits, boardMask);
	//
	// rotatedPlateVals = rotatePlate(originalPlateVals, direction);
	//
	//
	//
	// boardMask = 1 << shift;
	// boardBits = extractBitPlateVals(rotatedPlateVals, boardMask);
	// secondBoard |= boardBits;
    }

    private long rotateBits(long incompleteBoard, long boardCopy,
	    long boardMask, byte direction) {
	if (direction == Move.CLOCKWISE) {
	    // boardMask &= boardCopy;
	    incompleteBoard |= ((boardMask & boardCopy) << 2);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) << 7);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) << 12);
	    boardMask = boardMask << 4;
	    incompleteBoard |= ((boardMask & boardCopy) >> 5);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) << 0);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) << 5);
	    boardMask = boardMask << 4;
	    incompleteBoard |= ((boardMask & boardCopy) >> 12);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) >> 7);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) >> 2);
	} else {
	    incompleteBoard |= ((boardMask & boardCopy) << 12);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) << 5);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) >> 2);
	    boardMask = boardMask << 4;
	    incompleteBoard |= ((boardMask & boardCopy) << 7);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) << 0);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) >> 7);
	    boardMask = boardMask << 4;
	    incompleteBoard |= ((boardMask & boardCopy) << 2);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) >> 5);
	    boardMask = boardMask << 1;
	    incompleteBoard |= ((boardMask & boardCopy) >> 12);
	}
	return incompleteBoard;
    }

    private int[][] extractArrayPlateVals(long boardBits, long boardMask) {
	int[][] plateVals = new int[3][3];
	for (int y = 2; y >= 0; --y) {
	    for (int x = 2; x >= 0; --x) {
		plateVals[x][y] = (boardBits & boardMask) != 0 ? 1 : 0;
		boardMask <<= 1;
	    }
	    boardMask <<= 3;
	}
	return plateVals;
    }

    private int[][] rotatePlate(int[][] originalPlateVals, byte direction) {
	int[][] rotatedPlate = new int[3][3];
	if (direction == Move.CLOCKWISE) {
	    for (int x = 0; x < 3; x++)
		for (int y = 0; y < 3; y++) {
		    rotatedPlate[x][y] = originalPlateVals[y][2 - x];
		}
	} else {
	    for (int x = 0; x < 3; x++)
		for (int y = 0; y < 3; y++) {
		    rotatedPlate[x][y] = originalPlateVals[2 - y][x];
		}
	}
	return rotatedPlate;
    }

    private long extractBitPlateVals(int[][] arrayPlateVals, long boardMask) {
	long boardBits = 0;
	for (int y = 2; y >= 0; --y) {
	    for (int x = 2; x >= 0; --x) {
		if (arrayPlateVals[x][y] == 1)
		    boardBits |= boardMask;
		boardMask <<= 1;
	    }
	    boardMask <<= 3;
	}
	return boardBits;
    }
}
